Coverage Report

Created: 2024-12-26 12:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\tools.proto\tools.proto\compiler\src\gen\base\message.rs
Line
Count
Source
1
// Copyright (c) 2024, BlockProject 3D
2
//
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without modification,
6
// are permitted provided that the following conditions are met:
7
//
8
//     * Redistributions of source code must retain the above copyright notice,
9
//       this list of conditions and the following disclaimer.
10
//     * Redistributions in binary form must reproduce the above copyright notice,
11
//       this list of conditions and the following disclaimer in the documentation
12
//       and/or other materials provided with the distribution.
13
//     * Neither the name of BlockProject 3D nor the names of its contributors
14
//       may be used to endorse or promote products derived from this software
15
//       without specific prior written permission.
16
//
17
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29
use crate::compiler::message::{Field, FieldType, Message, Referenced};
30
use crate::compiler::structure::FixedFieldType;
31
use crate::compiler::util::types::TypeMapper;
32
use crate::gen::base::map::TypePathMapper;
33
use crate::gen::base::Error;
34
use crate::gen::codec::CodecMap;
35
use crate::gen::template::Template;
36
use crate::model::protocol::Endianness;
37
38
pub trait Utilities: crate::gen::base::structure::Utilities {
39
    fn get_value_type(endianness: Endianness, ty: FixedFieldType) -> &'static str;
40
    fn get_value_type_inline(endianness: Endianness, ty: FixedFieldType) -> &'static str;
41
    fn gen_struct_ref_type(type_name: &str) -> String;
42
    fn gen_message_ref_type(type_name: &str) -> String;
43
}
44
45
pub struct Templates<'fragment, 'variable> {
46
    pub template: Template<'fragment, 'variable>,
47
    pub codec_map: &'fragment CodecMap<'fragment, 'variable>,
48
}
49
50
impl<'fragment, 'variable> Templates<'fragment, 'variable> {
51
279
    pub fn get(&self, codec: &str) -> Result<&Template<'fragment, 'variable>, Error> {
52
279
        self.codec_map.get(codec).ok_or_else(|| 
Error::CodecNotFound(codec.into())0
)
53
279
    }
54
}
55
56
57
pub fn gen_msg_field_decl<U: Utilities, T: TypeMapper>(
57
57
    field: &Field,
58
57
    templates: &Templates,
59
57
    type_path_map: &TypePathMapper<T>,
60
57
) -> Result<String, Error> {
61
57
    let codec_template = templates.get(field.codec())
?0
;
62
57
    let mut scope = templates.template.scope();
63
57
    scope.var("name", &field.name).var("description", "").var_d("info", field);
64
57
    let msg_type = match &field.ty {
65
4
        FieldType::Fixed(ty) => U::get_field_type(ty.ty).into(),
66
13
        FieldType::Ref(v) => match v {
67
10
            Referenced::Struct(v) => U::gen_struct_ref_type(&type_path_map.get(v)),
68
3
            Referenced::Message(v) => U::gen_message_ref_type(&type_path_map.get(v)),
69
        },
70
11
        FieldType::Buffer => codec_template.render("decl", &["buffer"]).map_err(Error::Codec)
?0
,
71
7
        FieldType::SizedBuffer(v) => codec_template
72
7
            .scope()
73
7
            .var("codec", U::get_value_type(field.endianness, v.ty))
74
7
            .render("decl", &["sized_buffer"])
75
7
            .map_err(Error::Codec)
?0
,
76
4
        FieldType::FixedContainer(_) => scope.render("", &["list"]).unwrap(),
77
4
        FieldType::Union(v) => scope.var("type_name", type_path_map.get(&v.r)).render("", &["union"]).unwrap(),
78
10
        FieldType::Container(_) => scope.render("", &["list"]).unwrap(),
79
0
        FieldType::Payload => codec_template.render("decl", &["payload"]).map_err(Error::Codec)?,
80
4
        FieldType::SizedContainer(_) => scope.render("", &["list"]).unwrap(),
81
    };
82
57
    let msg_type = match field.optional {
83
10
        true => codec_template.scope().var("msg_type", msg_type).render("decl", &["option"]).map_err(Error::Codec)
?0
,
84
47
        false => msg_type,
85
    };
86
57
    Ok(scope.var("type", msg_type).render("decl", &["field"]).unwrap())
87
57
}
88
89
68
fn gen_message_array_field<U: Utilities, T: TypeMapper>(
90
68
    templates: &Templates,
91
68
    function: &str,
92
68
    field: &Field,
93
68
    type_path_map: &TypePathMapper<T>,
94
68
) -> Result<Option<String>, Error> {
95
68
    let mut scope = templates.template.scope();
96
68
    let codec_template = templates.get(field.codec())
?0
;
97
68
    scope
98
68
        .var("name", &field.name)
99
68
        .var(
100
68
            "description",
101
68
            field.description.as_ref().map(U::gen_description).unwrap_or("".into()),
102
68
        )
103
68
        .var_d("info", field);
104
68
    let value = match &field.ty {
105
6
        FieldType::FixedContainer(v) => {
106
6
            scope
107
6
                .var("item_type", type_path_map.get(&v.item_type))
108
6
                .var("codec", U::get_value_type(field.endianness, v.ty));
109
6
            let type_name = codec_template
110
6
                .scope()
111
6
                .var("item_type", type_path_map.get(&v.item_type))
112
6
                .var("codec", U::get_value_type(field.endianness, v.ty))
113
6
                .render("decl", &["fixed_container"])
114
6
                .map_err(Error::Codec)
?0
;
115
6
            Some(scope.var("type_name", type_name).render(function, &["array"]).unwrap())
116
        }
117
15
        FieldType::Container(v) => {
118
15
            scope
119
15
                .var("item_type", type_path_map.get(&v.item_type))
120
15
                .var("codec", U::get_value_type(field.endianness, v.ty));
121
15
            let type_name = codec_template
122
15
                .scope()
123
15
                .var("item_type", type_path_map.get(&v.item_type))
124
15
                .var("codec", U::get_value_type(field.endianness, v.ty))
125
15
                .render("decl", &["container"])
126
15
                .map_err(Error::Codec)
?0
;
127
15
            Some(scope.var("type_name", type_name).render(function, &["list"]).unwrap())
128
        }
129
5
        FieldType::SizedContainer(v) => {
130
5
            scope
131
5
                .var("item_type", type_path_map.get(&v.item_type))
132
5
                .var("codec", U::get_value_type(field.endianness, v.ty));
133
5
            let type_name = codec_template
134
5
                .scope()
135
5
                .var("item_type", type_path_map.get(&v.item_type))
136
5
                .var("codec", U::get_value_type(field.endianness, v.ty))
137
5
                .var("size_codec", U::get_value_type(field.endianness, v.size_ty))
138
5
                .render("decl", &["sized_container"])
139
5
                .map_err(Error::Codec)
?0
;
140
5
            Some(scope.var("type_name", type_name).render(function, &["list"]).unwrap())
141
        }
142
42
        _ => None,
143
    };
144
68
    Ok(value)
145
68
}
146
147
39
pub fn gen_message_array_type_decls<U: Utilities, T: TypeMapper>(
148
39
    templates: &Templates,
149
39
    function: &str,
150
39
    msg: &Message,
151
39
    type_path_map: &TypePathMapper<T>,
152
39
) -> Result<String, Error> {
153
39
    msg.fields
154
39
        .iter()
155
68
        .filter_map(|field| gen_message_array_field::<U, T>(templates, function, field, type_path_map).transpose())
156
39
        .collect::<Result<Vec<String>, Error>>()
157
39
        .map(|v| v.join(""))
158
39
}
159
160
31
pub fn generate<'variable, U: Utilities, T: TypeMapper>(
161
31
    mut templates: Templates<'_, 'variable>,
162
31
    msg: &'variable Message,
163
31
    type_path_map: &TypePathMapper<T>,
164
31
) -> Result<String, Error> {
165
31
    templates.template.var("msg_name", &msg.name);
166
31
    templates.template.var(
167
31
        "msg_description",
168
31
        msg.description.as_ref().map(U::gen_description).unwrap_or("".into()),
169
31
    );
170
31
    let fields = msg
171
31
        .fields
172
31
        .iter()
173
57
        .map(|v| gen_msg_field_decl::<U, T>(v, &templates, type_path_map))
174
31
        .collect::<Result<Vec<String>, Error>>()
?0
175
31
        .join("");
176
31
    let mut code = templates.template.scope().var("fields", fields).render("", &["decl"]).unwrap();
177
31
    code += "\n";
178
31
    code += &gen_message_array_type_decls::<U, T>(&templates, "typealias", msg, type_path_map)
?0
;
179
31
    Ok(code)
180
31
}